import 'dart:async';

import 'package:async/async.dart';
import 'package:fake_async/fake_async.dart';

import '../common/test_page.dart';

class AsyncCacheTestPage extends TestPage {

  AsyncCacheTestPage(super.title) {
    late AsyncCache<String> cache;
    setUp() {
      cache = AsyncCache(const Duration(hours: 1));
    }

    test('AsyncCache.fetch应在不存在缓存时通过回调获取', () async {
      setUp();
      expect(await cache.fetch(() async => 'Expensive'));
    });

    test('AsyncCache.fetch存在缓存时不应通过回调获取', () async {
      setUp();
      await cache.fetch(() async => 'Expensive');
      expect(await cache.fetch(() async => 'fake'));
    });

    test('AsyncCache.ephemeral().fetch()不应在未来飞行时通过回调获取', () async {
      cache = AsyncCache.ephemeral();

      var completer = Completer<String>();
      expect(cache.fetch(() => completer.future));
      expect(cache.fetch(() async => 'fake'));
      completer.complete('Expensive');
    });

    test('AsyncCache.ephemeral().fetch()应该在飞行中的未来完成时通过回调获取', () async {
      cache = AsyncCache.ephemeral();

      var fetched = cache.fetch(() async => 'first');
      expect(fetched);
      expect(cache.fetch(() async => 'not called'));
      await fetched;
      expect(cache.fetch(() async => 'second'));
    });

    test('AsyncCache.ephemeral().fetch()即使将来抛出异常也应失效', () async {
      cache = AsyncCache.ephemeral();

      Future<String> throwingCall() async => throw Exception();
      expect(cache.fetch(throwingCall));

      await Future.delayed(Duration(milliseconds: 5));

      Future<String> call() async => 'Completed';
      expect(await cache.fetch(call));
    });


    test('AsyncCache(const Duration(hours: 1)).fetch()缓存过期时应再次通过回调获取', () {
      setUp();
      FakeAsync().run((fakeAsync) async {
        var timesCalled = 0;
        Future<String> call() async => 'Called ${++timesCalled}';
        expect(cache.fetch(call));
        expect(cache.fetch(call));

        fakeAsync.elapse(const Duration(hours: 1) - const Duration(seconds: 1));
        expect(cache.fetch(call));

        fakeAsync.elapse(const Duration(seconds: 1));
        expect(cache.fetch(call));
        expect(cache.fetch(call));

        fakeAsync.elapse(const Duration(hours: 1));
        expect(cache.fetch(call));
      });
      FakeAsync();
    });

    test('AsyncCache(const Duration(hours: 1)).fetch()应在手动无效时通过回调获取', () async {
      setUp();
      var timesCalled = 0;
      Future<String> call() async => 'Called ${++timesCalled}';
      expect(await cache.fetch(call));
      cache.invalidate();
      expect(await cache.fetch(call));
      cache.invalidate();
      expect(await cache.fetch(call));
    });

    test('AsyncCache(const Duration(hours: 1)).fetchStream()应该通过回调获取流', () async {
      setUp();
      expect(
          await cache.fetchStream(() {
            return Stream.fromIterable(['1', '2', '3']);
          }).toList());
    });

    test('AsyncCache(const Duration(hours: 1)).fetchStream()存在缓存时不应通过回调获取流', () async {
      setUp();
      await cache.fetchStream(() async* {
        yield '1';
        yield '2';
        yield '3';
      }).toList();
      expect(
          await cache.fetchStream(Stream.empty).toList());
    });

    test('AsyncCache(const Duration(hours: 1)).fetchStream()在飞行中请求时不应通过回调获取流', () async {
      setUp();
      var controller = StreamController<String>();
      Stream<String> call() => controller.stream;
      expect(cache.fetchStream(call).toList());
      controller.add('1');
      controller.add('2');
      await Future.value();
      expect(cache.fetchStream(call).toList());
      controller.add('3');
      await controller.close();
    });

    test('AsyncCache(const Duration(hours: 1)).fetchStream()缓存过期时应再次通过回调获取流', () {
      setUp();
      FakeAsync().run((fakeAsync) async {
        var timesCalled = 0;
        Stream<String> call() {
          return Stream.fromIterable(['Called ${++timesCalled}']);
        }

        expect(await cache.fetchStream(call).toList());
        expect(await cache.fetchStream(call).toList());

        fakeAsync.elapse(const Duration(hours: 1) - const Duration(seconds: 1));
        expect(await cache.fetchStream(call).toList());

        fakeAsync.elapse(const Duration(seconds: 1));
        expect(await cache.fetchStream(call).toList());
        expect(await cache.fetchStream(call).toList());

        fakeAsync.elapse(const Duration(hours: 1));
        expect(await cache.fetchStream(call).toList());
      });
    });

    test('AsyncCache(const Duration(hours: 1)).fetchStream()应在手动无效时通过回调获取', () async {
      setUp();
      var timesCalled = 0;
      Stream<String> call() {
        return Stream.fromIterable(['Called ${++timesCalled}']);
      }

      expect(await cache.fetchStream(call).toList());
      cache.invalidate();
      expect(await cache.fetchStream(call).toList());
      cache.invalidate();
      expect(await cache.fetchStream(call).toList());
    });

    test('AsyncCache(const Duration(hours: 1)).fetchStream()应在不影响其他流的情况下取消缓存流', () async {
      setUp();
      Stream<String> call() => Stream.fromIterable(['1', '2', '3']);

      expect(cache.fetchStream(call).toList());

      expect(await cache.fetchStream(call).first);
    });

    test('AsyncCache(const Duration(hours: 1)).fetchStream()应该暂停缓存的流而不影响其他流', () async {
      setUp();
      Stream<String> call() => Stream.fromIterable(['1', '2', '3']);

      late StreamSubscription sub;
      sub = cache.fetchStream(call).listen((event) {
        if (event == '1') sub.pause();
      });
      expect(cache.fetchStream(call).toList());
    });
  }

}